﻿/*
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 */

#include "CmdCtrl.h"
#include <QDir>
#include <QSettings>
#include <windows.h>
#include <usbioctl.h>
#include "SecureLink/Channel.h"
#include "BurnToolCommon/Global.h"

namespace  {
    const int HEAD_PARITY_VALUE_TWO = 2;
    const int NUMBER_FIFTY = 15;
    const int NUMBER_TEN = 10;
    const int ADDR_SIZE = 6;
    const int PID_LEN = 16;
}

CmdCtrl::CmdCtrl(QObject *parent)
    : QObject(parent),
    m_isFormal(TRUE),
    m_eraseMode(0),
    m_isEraseAll(FALSE),
    m_isForceRead(FALSE),
    m_onlyBurnToolLog(FALSE),
    m_forceReadTime(NUMBER_TEN),
    m_isShow(FALSE)
{
    qRegisterMetaType<LPVOID>("LPVOID");
    qRegisterMetaType<WPARAM>("WPARAM");
    qRegisterMetaType<LPARAM>("LPARAM");
    qRegisterMetaType<long int>("long int");
}

CmdCtrl::~CmdCtrl()
{
    if (m_burnCtrl != nullptr) {
        delete m_burnCtrl;
        m_burnCtrl = nullptr;
    }
}

CmdCtrl* CmdCtrl::GetInstence()
{
    static CmdCtrl cmdCtrl;
    return &cmdCtrl;
}

UINT CmdCtrl::InitConfig()
{
    Global::currentChipTypeId = m_chipType;
    Global::SaveCfg();
    Global::ReadCfg();
    InitBurnCtrl();
    if (m_isGetHidDevice) {
        GetHidDevice();
        return ERR_SOP_GET_HID_DEVICE_SUCCESS;
    }
    if (m_eraseMode < 0 || m_eraseMode > 2) { // 0:normal 1:erase all 2:no erase
        SlotPassParamError(0);
        return ERR_SOP_INVALID_PARAMETERS;
    }
    if (m_forceReadTime != 10) { // 10
        m_isForceRead = FALSE;
        SlotPassParamError(0);
        return ERR_SOP_INVALID_PARAMETERS;
    }
    QMap<int, BIN_INFO> mapBinInfo;
    BIN_INFO_ARRAY infoArray;
    QString version;
    if (((!m_isExport && !m_isEraseAll) || ((m_isExport || m_isEraseAll) &&
                                            (!Type3X()))) && !(QFile(m_strPath).exists())) {
        SlotPassParamError(1);
        return ERR_SOP_INVALID_PARAMETERS;
    }
    if (!QFileInfo(m_strPath).isFile()) {
        SlotPassParamError(1);
        return ERR_SOP_INVALID_PARAMETERS;
    }
    if (YModemType()) {
        SetWifiBurnCtrlPram();
    } else if (Type3X()) {
        InitConfig3X(infoArray, version, mapBinInfo);
    } else if (Global::IsUSBType(m_chipType)) {
        SetUsbBurnCtrlPram();
    } else if (JlinkType()) {
        DataConvertor::AnalysMultifBin(m_strPath, infoArray, version, isEncrypt, encryptBuffer);
        InitConfigBinInfo(infoArray, mapBinInfo);
        m_burnCtrl->InitBurnCmdByArray(mapBinInfo);
    } else {
        m_burnCtrl->SetFilePath(0, m_strPath);
    }
    m_burnCtrl->SetSecConn(m_isSecConn);
    InitConfigSub();
    return ERR_SOP_SUCCESS;
}

void CmdCtrl::InitConfig3X(BIN_INFO_ARRAY &infoArray, QString &version, QMap<int, BIN_INFO> &mapBinInfo)
{
    m_burnCtrl->SetBurnTimeout(m_connectTime);
    if (!m_isExport && !m_isEraseAll) {
        DataConvertor::AnalysMultifBin(m_strPath, infoArray, version, isEncrypt, encryptBuffer);
        InitConfigBinInfo(infoArray, mapBinInfo);
        m_burnCtrl->SetComNum(m_comNum);
        m_burnCtrl->InitBurnCmdByArray(mapBinInfo);
    }
}

UINT CmdCtrl::SleInitConfig()
{
    m_burnCtrlSle = new BurnCtrlSleClass::BurnCtrlSle();
    m_burnCtrlSle->SetOneTimeBurnFlag(true);
    MyThreadClass::MyThread *sleThread = new MyThreadClass::MyThread();
    m_burnCtrlSle->moveToThread(sleThread);
    connect(sleThread, &QThread::finished, m_burnCtrlSle, &QObject::deleteLater);
    connect(this, &CmdCtrl::SignalSendMsgSle, m_burnCtrlSle, &BurnCtrlSleClass::BurnCtrlSle::SlotSendMsg);
    connect(m_burnCtrlSle, &BurnCtrlSleClass::BurnCtrlSle::SignalInsertPlainText, this, &CmdCtrl::SlotInsertPlainText);
    connect(m_burnCtrlSle, &BurnCtrlSleClass::BurnCtrlSle::SignalSetProgress, this, &CmdCtrl::SlotSetProgress);

    sleThread->start();
    QStringList addList = m_strSleAddr.split(':');
    if (addList.size() != ADDR_SIZE) {
        return ERR_SOP_INVALID_PARAMETERS;
    }
    for (int i = 0; i < addList.size(); i++) {
        bool ok = false;
        m_sleDeviceInfo.addr[i] = addList[i].toUShort(&ok, 0x16);
    }
    m_sleDeviceInfo.addrType = m_strSleAddrType.toUShort();
    m_burnCtrlSle->SetDeviceInfo(m_sleDeviceInfo);
    HidDeviceInfo deviceInfo;
    deviceInfo.vid = m_vid;
    deviceInfo.pid = m_pid;
    deviceInfo.usage = m_usage;
    deviceInfo.usagePage = m_usagePage;
    QMap<int, BIN_INFO> mapBinInfo;
    BIN_INFO_ARRAY infoArray;
    QString version;
    DataConvertor::AnalysMultifBin(m_strPath, infoArray, version, isEncrypt, encryptBuffer);
    InitConfigBinInfo(infoArray, mapBinInfo);
    m_burnCtrlSle->InitBurnCmdByArray(mapBinInfo);
    CHAR *device = HidEnumerateInterface(deviceInfo);
    if (device != nullptr) {
        m_burnCtrlSle->SetDeviceInterface(device);
        emit SignalSendMsgSle(static_cast<long int>(SleThreadMsg::SLE_THREAD_MSG_OPEN), 0, 0);
    }
    return ERR_SOP_SUCCESS;
}

CHAR *CmdCtrl::HidEnumerateInterface(HidDeviceInfo deviceInfo) const
{
    QMap <QString, HidDeviceInfo> mapName;
    mapName.clear();
    QString str = Global::HidEnumerate(mapName);
    if (!str.isEmpty()) {
        SlotInsertPlainText(str);
    }
    for (auto it = mapName.begin(); it != mapName.end(); it++) {
        if (deviceInfo.vid == it.value().vid && deviceInfo.pid == it.value().pid &&
            deviceInfo.usagePage == it.value().usagePage && deviceInfo.usage == it.value().usage) {
            return it.key().toUtf8().data();
        }
    }
    return nullptr;
}

bool CmdCtrl::YModemType() const
{
    if (m_chipType == ChipType::WIFI5GNB || m_chipType == ChipType::WIFIDW31 ||
        m_chipType == ChipType::DFU || m_chipType == ChipType::BS2X ||
        Global::IsWs63(m_chipType) || m_chipType == ChipType::WS53 ||
        m_chipType == ChipType::WIFI4GCAT1 || m_chipType == ChipType::SW39 ||
        m_chipType == ChipType::SW21 || m_chipType == ChipType::HI3372 ||
        m_chipType == ChipType::HI3322 || m_chipType == ChipType::COM2982) {
        return true;
    }
    return false;
}

bool CmdCtrl::JlinkType() const
{
    if (m_chipType == ChipType::JLINKBS25 || m_chipType == ChipType::JLINKBS2X ||
            m_chipType == ChipType::JLINK2982) {
        return true;
    }
    return false;
}

bool CmdCtrl::Type3X() const
{
    if (m_chipType == ChipType::SPARTA || m_chipType == ChipType::MELODY || m_chipType == ChipType::COMBS25) {
        return true;
    }
    return false;
}

void CmdCtrl::SetWifiBurnCtrlPram()
{
    QMap<int, BIN_INFO> mapBinInfo;
    BIN_INFO_ARRAY infoArray;
    QString version;
    DataConvertor::AnalysMultifBin(m_strPath, infoArray, version, isEncrypt, encryptBuffer);
    InitConfigBinInfo(infoArray, mapBinInfo);
    m_burnCtrl->SetReadEfuse(m_readEfuse);
    m_burnCtrl->SetStartBit(m_startBit);
    m_burnCtrl->SetBitWidth(m_bitWidth);
    m_burnCtrl->SetPackageSize(m_packageSize);
    m_burnCtrl->SetIsSwitchAfterLoader(m_switchAfterLoader);
    m_burnCtrl->SetIsEncrypt(isEncrypt);
    m_burnCtrl->SetEncryptBuffer(encryptBuffer.bufferData);
    m_burnCtrl->SetEncryptBufferLen(encryptBuffer.bufferLen);
    m_burnCtrl->InitBurnCmdByArray(mapBinInfo);
    m_burnCtrl->FwpkgVersion(version);
}

void CmdCtrl::SetUsbBurnCtrlPram()
{
    QMap<int, BIN_INFO> mapBinInfo;
    BIN_INFO_ARRAY infoArray;
    QString version;
    DataConvertor::AnalysMultifBin(m_strPath, infoArray, version, isEncrypt, encryptBuffer);
    InitConfigBinInfo(infoArray, mapBinInfo);
    HidDeviceInfo deviceInfo;
    deviceInfo.vid = m_vid;
    deviceInfo.pid = m_pid;
    deviceInfo.serialName = m_usbDevicePathID;
    deviceInfo.locationInfo = m_usbLocation;
    deviceInfo.usage = m_usage;
    deviceInfo.usagePage = m_usagePage;
    if (m_isAutoDfu) {
        m_burnCtrl->SetIsAutoDfu(DfuBurnMode::AUTO_DFU);
    } else if (m_isHidDfu) {
        m_burnCtrl->SetIsAutoDfu(DfuBurnMode::HID_DFU);
    } else {
        m_burnCtrl->SetIsAutoDfu(DfuBurnMode::DFU);
    }

    m_burnCtrl->SetUsbDeviceInfo(deviceInfo);
    m_burnCtrl->InitBurnCmdByArray(mapBinInfo);
}

void CmdCtrl::InitConfigBinInfo(const BIN_INFO_ARRAY &infoArray, QMap<int, BIN_INFO> &mapBinInfo) const
{
    int startImport = mapBinInfo.size();
    int mapIndex = 0;
    for (int i = 0; i < infoArray.GetSize(); i++) {
        if (!m_burnFileName.isEmpty() && m_burnFileName.indexOf(infoArray.GetAt(i).fileName.toLower()) == -1) {
            continue;
        }
        mapBinInfo[startImport + mapIndex++] = infoArray.GetAt(i);
    }
}

void CmdCtrl::InitBurnCtrl()
{
    BurnCtrlFaction faction;
    m_burnCtrl = faction.GetBurnCtrl(m_chipType);
    connect(m_burnCtrl, &BurnCtrl::SignalOpenComFail, this, &CmdCtrl::SlotOpenComFail);
    connect(m_burnCtrl, &BurnCtrl::SignalBurnFileFinish, this, &CmdCtrl::SlotBurnFileFinish);
    connect(m_burnCtrl, &BurnCtrl::SignalBurnFileFailed, this, &CmdCtrl::SlotBurnFileFailed);
    connect(m_burnCtrl, &BurnCtrl::SignalInsertPlainText, this, &CmdCtrl::SlotInsertPlainText);
    m_burnCtrl->SetOneTimeBurnFlag(true);
    m_burnCtrl->StartWiteMonitoring();
    m_burnCtrl->SwitchBoot(m_bootType);
    m_burnCtrl->SetFileInfo(m_strPath);
    m_burnCtrl->SetAutoBurnFlag(true);
    m_burnCtrl->SetReopenComFlag(false);
    m_burnCtrl->SetEraseMode(m_eraseMode);
    m_burnCtrl->SetOnlyEraseAll(m_isEraseAll);
    if (m_period == 0) {
        m_period = 10; // 设置为默认值10
    }
    m_burnCtrl->SetPeriod(m_period);
    m_burnCtrl->SetBurnTimeout(m_connectTime / NUMBER_FIFTY * m_period);
    m_burnCtrl->SetOnlyBurnToolLog(m_onlyBurnToolLog);
}

void CmdCtrl::InitConfigSub()
{
    if (!m_ipAddr.isEmpty()) {
        m_burnCtrl->SetPortType(Base::TCP_PORT);
        m_burnCtrl->SetIpSetting(m_ipAddr, m_ipPort);
        m_baud = OPEN_SERIAL_PORT_DEFAULT_BAUD_RATE;
    }
    if (YModemType()) {
        SEBOOT_SHAKE_HANDS_FRAME *head = reinterpret_cast<SEBOOT_SHAKE_HANDS_FRAME *>(Global::shakeHandSignal);
        head->baudRate = m_baud;
        head->dataBits = m_databits;
        if (m_stopbits == 0) {
            head->stopBits = 1;
        } else if (m_stopbits == 2) {  // Stop bit 2
            head->stopBits = 2; // Stop bit 2
        } else {
            head->stopBits = 1;
        }

        if (m_parity == QSerialPort::NoParity) {
            head->parity = 0;
        } else if (m_parity == QSerialPort::OddParity) {
            head->parity = 1;
        } else {
            head->parity = HEAD_PARITY_VALUE_TWO;
        }
        head->flowctrl = m_flowCtrl;
        head->checkSum = ChannelClass::Channel::SopCrc16(reinterpret_cast<PBYTE>(Global::shakeHandSignal),
            SHAKE_HAND_SIGNAL_LEN - 2); // crc16 len 2
    } else {
        m_burnCtrl->UpdateComSetting(m_baud, m_databits, m_stopbits, m_parity, m_flowCtrl);
    }
    m_burnCtrl->SetForceReadFlag(m_isForceRead);
    m_burnCtrl->SetForceReadTime(m_forceReadTime);
    m_burnCtrl->SetComNum(m_comNum);
    m_burnCtrl->SetResetAfterBurn(m_isReset);
    m_burnCtrl->SetExportAfterBurn(m_isExport);
    m_burnCtrl->SetExportPath(m_exportTarget);
    m_burnCtrl->SetExportAddr(m_exportAddr);
    m_burnCtrl->SetExportSize(m_exportSize);
    m_burnCtrl->InitBurnCmd();
}

UINT CmdCtrl::StartProc()
{
    UINT ret = 0;
    if (m_burnCtrl != nullptr && (m_chipType == ChipType::SPARTA || m_chipType == ChipType::MELODY ||
                                  m_chipType == ChipType::USBBS25 || m_chipType == ChipType::COMBS25 ||
                                  m_chipType == ChipType::USBBS2X || m_chipType == ChipType::JLINKBS25 ||
                                  m_chipType == ChipType::JLINKBS2X || m_chipType == ChipType::USB2982 ||
                                  m_chipType == ChipType::JLINK2982 || m_chipType == ChipType::USBBS27)) {
        ExportInfo *exportInfo = new ExportInfo();
        if (exportInfo == nullptr) {
            return ret;
        }
        exportInfo->addr = DataConvertor::Str2U32DecHex(m_exportAddr);
        exportInfo->len = DataConvertor::Str2U32DecHex(m_exportSize);
        exportInfo->path = m_exportTarget;
        if (m_isExport) {
            emit SignalSendMsg(static_cast<long>(WiteThreadMsg::WITE_THREAD_MSG_EXPORT),
                               reinterpret_cast<WPARAM>(exportInfo), sizeof(exportInfo));
        } else if (m_isEraseAll) {
            emit SignalSendMsg(static_cast<long>(WiteThreadMsg::WITE_THREAD_MSG_ERASE_ALL),
                               reinterpret_cast<WPARAM>(nullptr), 0);
        } else {
            emit SignalSendMsg(static_cast<long>(WiteThreadMsg::WITE_THREAD_MSG_STARTCONNECT), 0, 0);
        }
        return ret;
    }

    if (m_burnCtrl != nullptr) {
        ret = m_burnCtrl->ConfigComPort() ? ERR_SOP_SUCCESS : static_cast<int>(ERR_COM_INVALID);
        if (ret == ERR_SOP_SUCCESS) {
            emit SignalSendMsg(static_cast<long>(WiteThreadMsg::WITE_THREAD_MSG_STARTCONNECT), 0, 0);
        } else {
            emit SignalSendMsg(static_cast<long>(WiteThreadMsg::WITE_THREAD_MSG_EXIT), ret, sizeof (UINT));
        }
    }
    return ret;
}

void CmdCtrl::SetBaud(UINT baud)
{
    m_baud = baud;
}

void CmdCtrl::SetBootType(BootType type)
{
    m_bootType = type;
}

void CmdCtrl::SetChipType(ChipType type)
{
    if (!m_isChipType) {
        m_chipType = type;
    }
}

void CmdCtrl::SetIsChipType(bool chipType)
{
    m_isChipType = chipType;
}

void CmdCtrl::SetForceReadTime(UINT time)
{
    m_forceReadTime = time;
}

void CmdCtrl::SetConnectTimeout(UINT time)
{
    m_connectTime = time;
}

void CmdCtrl::SetEraseMode(int isEraseMode)
{
    m_eraseMode = isEraseMode;
}

void CmdCtrl::SetOnlyEraseAll(bool isEraseAll)
{
    m_isEraseAll = isEraseAll;
}

void CmdCtrl::SetForceRead(bool isForceRead)
{
    m_isForceRead = isForceRead;
}

void CmdCtrl::SetFormal(bool isFormal)
{
    m_isFormal = isFormal;
}

void CmdCtrl::SetShow(BOOL isShow)
{
    m_isShow = isShow;
}

void CmdCtrl::SetOnlyBurnToolLog(bool onlyBurnToolLog)
{
    m_onlyBurnToolLog = onlyBurnToolLog;
}

void CmdCtrl::SetPeriod(UINT period)
{
    m_period = period;
}

void CmdCtrl::SetBinPath(QString path)
{
    m_strPath = path;
}

void CmdCtrl::SetComNum(UINT comNum)
{
    m_comNum = comNum;
}

void CmdCtrl::SetAllowBurnFile(QString name)
{
    m_burnFileName.push_back(name);
}

void CmdCtrl::SetResetAfterBurn(bool reset)
{
    m_isReset = reset;
}

void CmdCtrl::SetIpAddr(QString ip)
{
    m_ipAddr = ip;
}

void CmdCtrl::SetIpPort(UINT port)
{
    m_ipPort = port;
}

void CmdCtrl::SetExportAfterBurn(bool isExport)
{
    m_isExport = isExport;
}

void CmdCtrl::SetExportTarget(QString target)
{
    m_exportTarget = target;
}

void CmdCtrl::SetExportAddr(QString addr)
{
    m_exportAddr = addr;
}

void CmdCtrl::SetExportSize(QString size)
{
    m_exportSize = size;
}

void CmdCtrl::SetDfuVid(uint32_t vid)
{
    m_vid = vid;
}

void CmdCtrl::SetDfuPid(uint32_t pid)
{
    m_pid = pid;
}

void CmdCtrl::SetDfuPathID(QString devicePathID)
{
    m_usbDevicePathID = devicePathID;
}

void CmdCtrl::SetDfuLocation(QString deviceLocation)
{
    m_usbLocation = deviceLocation;
}

void CmdCtrl::SetDfuUsage(uint32_t usage)
{
    m_usage = usage;
}

void CmdCtrl::SetDfuUsagePage(uint32_t usagePage)
{
    m_usagePage = usagePage;
}

void CmdCtrl::SetIsReadEfuse(bool readEfuse)
{
    m_readEfuse = readEfuse;
}

void CmdCtrl::SetStartBit(QString startbit)
{
    m_startBit = startbit;
}

void CmdCtrl::SetBitWidth(QString bitWidth)
{
    m_bitWidth = bitWidth;
}

void CmdCtrl::SetPackageSize(UINT packageSize)
{
    m_packageSize = packageSize;
}

void CmdCtrl::SetSwitchAfterLoader()
{
    m_switchAfterLoader = true;
}

void CmdCtrl::SetSleAddr(QString str)
{
    m_strSleAddr = str;
}

void CmdCtrl::SetSleAddrType(QString str)
{
    m_strSleAddrType = str;
}

void CmdCtrl::SetJLinkPath(QString path)
{
    m_jLinkPath = path;
    QSettings cfg(Global::GetAppConfigPath() + "\\config_" +
                  Global::ChipTypeIdToString(Global::currentChipTypeId) + "\\config.ini",
                  QSettings::IniFormat);
    cfg.setValue(CFG_JLINK_PATH, m_jLinkPath);
}

void CmdCtrl::SetIsAutoDfu(bool isAutoDfu)
{
    m_isAutoDfu = isAutoDfu;
}

void CmdCtrl::SetIsHidDfu(bool isHidDfu)
{
    m_isHidDfu = isHidDfu;
}

void CmdCtrl::SetIsGetHidDevice(bool isGetHidDevice)
{
    m_isGetHidDevice = isGetHidDevice;
}


int CmdCtrl::GetResult() const
{
    if (m_burnCtrl != nullptr) {
        return m_burnCtrl->GetResult();
    }
    return 0xff;
}

void CmdCtrl::SlotOpenComFail()
{
    RemoveTempDir();
    QTextStream qout(stdout);
    qout << stringClass.GetIdsString32812().arg(m_comNum) << endl;
}

void CmdCtrl::SlotBurnFileFinish()
{
    RemoveTempDir();
    QTextStream qout(stdout);
    qout << IDS_STRING_32879 << endl;
    qout << stringClass.GetIdsString32880() << endl;
    qout << IDS_STRING_32879 << endl;
}

void CmdCtrl::SlotBurnFileFailed()
{
    RemoveTempDir();
    QTextStream qout(stdout);
    qout << IDS_STRING_32879 << endl;
    qout << "Images burn failed" << endl;
    qout << IDS_STRING_32879 << endl;
}

void CmdCtrl::SlotInsertPlainText(const QString &editStr) const
{
    QTextStream qout(stdout);
    qout << editStr << endl;
}

void CmdCtrl::SlotSetProgress(double d) const
{
    QTextStream qout(stdout);
    qout << QString::number(d) + '\n';
}

void CmdCtrl::SlotPassParamError(int mode)
{
    RemoveTempDir();
    QTextStream qout(stdout);
    qout << IDS_STRING_32879 + IDS_STRING_32824;
    if (mode == 0) {
        qout << (stringClass.GetIdsString33278() + IDS_STRING_32824);
    } else if (mode == 1) {
        qout << (stringClass.GetIdsString33279() + IDS_STRING_32824);
    }
    qout << IDS_STRING_32879 + IDS_STRING_32824;
    emit SignalSendMsg(static_cast<long>(WiteThreadMsg::WITE_THREAD_MSG_EXIT), ERR_FAILURE, sizeof (UINT));
}

void CmdCtrl::PassParamErrorHint()
{
    SlotPassParamError(0);
}

void CmdCtrl::RemoveTempDir() const
{
    QString saveDir = QCoreApplication::applicationDirPath() + "/tempFile" + "_"
            + QString::number(m_comNum);
    QDir dir(saveDir);
    if (dir.exists()) {
        dir.removeRecursively();
    }
}

void CmdCtrl::GetHidDevice()
{
    QMap <QString, HidDeviceInfo> mapName;
    mapName.clear();
    Global::HidEnumerate(mapName);
    for (auto it = mapName.begin(); it != mapName.end(); it++) {
        QTextStream qout(stdout);
        qout << "Name:" + it.value().productName + ",PID:0x" + QString::number(it.value().pid, PID_LEN) +
                ",VID:0x" + QString::number(it.value().vid, PID_LEN) + ",Usage:0x" +
                QString::number(it.value().usage, PID_LEN) + ",UsagePage:0x" +
                QString::number(it.value().usagePage, PID_LEN) + "\n";
    }
    emit SignalSendMsg(static_cast<long>(WiteThreadMsg::WITE_THREAD_MSG_EXIT), ERR_FAILURE, sizeof (UINT));
}
